package org.lizar;


import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.lizar.config.Config;
import org.lizar.config.group.Group;
import org.lizar.config.i18.I18Msg;
import org.lizar.config.keys.Keys;
import org.lizar.controller.Controller;
import org.lizar.controller.Event;
import org.lizar.controller.staticsrc.ContentType;
import org.lizar.exception.LifecycleException;
import org.lizar.ioc.Container;
import org.lizar.ioc.Lifecycle;
import org.lizar.ioc.module.Module;
import org.lizar.ioc.module.ModuleManager;
import org.lizar.log.Log;
import org.lizar.log.Logger;
import org.lizar.util.StringHelper;



/**
 * 
 * @caution1 config the config.json file location in web.xml, or system will use default location /WEB-INF/config.json
 * @caution2 dao,plugin,event and all kinds of modules have only one instance in system.
 * 
 * @task1 startup system components:<br/>
 * 			two level components:module, {cell,plugin,event}<br/>
 * 			cell: lizar object<br/>
 * 			plugin: scheduled task<br/>
 * 			event: user request task<br/>
 * 			sequence: 1 model{container,controller,pluginManager},2 {cell,plugin,event}
 * @task2 <strong>receives</strong> all users' requests, controller <strong>dispatches</strong> the requests to proper event
 *  	  and then event <strong>handles</strong> the request.
 * 
 * */


@WebFilter(urlPatterns = {"/*"},
dispatcherTypes = {DispatcherType.REQUEST},
initParams = {@WebInitParam(name = "encoding", value = "UTF-8")}
)
public class Web implements Filter{
    
	private Log log=Logger.newInstance(this.getClass());
	

    public static Map<String,Event> events;
    
    public static Map<String,Module> modules;
    
    public static List<Lifecycle> lifecycle_list;
    
    public static Controller controller;
    
    public static Group group;

    public static Keys keys;
    
    public static I18Msg i18;
    
    public static org.lizar.config.Config cfg;
    
    public static ServletContext context;
    
    public static Container container;
    
    public static boolean debug=false;
    
    public static ModuleManager module_manager;

    
	/**
	 * starting all system components
	 * 
	 * if any exception happens, system start fails
	 * 
	 * */
	private void _init_property() throws Exception {
		log.info("..............................................................................");
		log.info("\t\t\tlizar is going to start");
		events=new HashMap<String,Event>();
		modules=new HashMap<String,Module>();
		lifecycle_list=new LinkedList<Lifecycle>();
		cfg=Config.instance();						//load global system configuration
		cfg.init_property();
		cfg.born();
		
		keys=Keys.instance();						//load cell configuration
		group=Group.instance();						//load cell configuration
		i18=I18Msg.instance();
		
		
		keys.persistence();							//read msg from file
		group.persistence();						//read group from file
		
		//search and create all the cells and init property
		container=Container.class.newInstance();	
		container.check_cells();
		
		keys.persistence();							//store msg to file
		group.persistence();						//store group to file
		i18.persistence();							//load msg from files
		
		module_manager=ModuleManager.instance();
		
		controller=Controller.instance();
		controller.init_property();
		controller.born();
		
		lifecycle_born();
		
		cfg.ready_to_check_changes();				//ready to check changes
	}
    
	private void lifecycle_born() throws LifecycleException, Exception{
		for(Lifecycle l:lifecycle_list){
			l.born();
		}
	}
	
	private void lifecycle_die() throws LifecycleException {
		for(Lifecycle l:lifecycle_list){
			l.die();
		}
	}

	/**
	 * Destruction of the servlet. <br>
	 * 
	 * stop all active components.
	 */
	@Override
	public void destroy() {
		log.info("..............................................................................");
		log.info("\t\t\tlizar is going to stop");
		try {
			controller.die();
		} catch (LifecycleException e2) {
			// TODO Auto-generated catch block
			log.error("Component Config has been stopped",e2);
		}
		
		
		try {
			cfg.die();
		} catch (LifecycleException e3) {
			// TODO Auto-generated catch block
			log.error("Component Config has been stopped",e3);
		}
		
		try {
			lifecycle_die();
		} catch (LifecycleException e3) {
			// TODO Auto-generated catch block
			log.error("Components Lifecycle has been stopped",e3);
		}
		
		log.info("..............................................................................");
	}


	@Override
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain arg2) throws IOException, ServletException {
		HttpServletRequest httpReq = (HttpServletRequest) request;
        HttpServletResponse httpResp = (HttpServletResponse) response;
        String path=StringHelper.isNull(httpReq.getServletPath())?"/":path_filter(httpReq.getServletPath());
        String post_fix=StringHelper.postfix(path).toLowerCase();
        
		if(ContentType.exists(post_fix)||"jsp".equals(post_fix)||"jsf".equals(post_fix)){
			arg2.doFilter(httpReq, httpResp);
		}else{
			controller.handle_event(httpReq, httpResp,path,post_fix);
		}
	}
	
	
	
	public String path_filter(String path){
		if(StringHelper.isNull(path))return "/";
		if(path.length()>1&&path.endsWith("/"))return path.substring(0, path.length()-1);
		return path;
	}


	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		// TODO Auto-generated method stub
		context=filterConfig.getServletContext();
		debug=false;
		log.info("Start to Run...");
		log.info("Base Dir:"+context.getRealPath("/"));
		try {
			_init_property();
		} catch (Exception e) {
			log.info("Framework init property failed, pls check....",e);
			return;
		}
	}
	
}
